home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / Newton Sample Code 1.1 / Connection Kit / Built In MetaDatas-1 / Date Book MetaData < prev    next >
Encoding:
Text File  |  1994-03-09  |  22.8 KB  |  718 lines  |  [TEXT/MPS ]

  1. /*
  2.     Copyright:    © 1993-1994 by Apple Computer, Inc., all rights reserved.
  3. */
  4.  
  5. //-------------------------------------------------------------------------------------------------
  6. //                        I M P O R T
  7. //-------------------------------------------------------------------------------------------------
  8. // These are the functions used to build entries for the 5 soups.
  9.  
  10. CalendarBuildMeeting :=    func( textFrame, store)
  11.     begin
  12.     local temp;
  13.     if textFrame.mtgStartDate and textFrame.startTime and textFrame.mtgDuration and textFrame.title then
  14.         begin
  15.         temp := {viewStationery:    'meeting};
  16.         temp.MetaDataSoupRef := store:GetSoup( "Calendar");
  17.         temp.mtgStartDate := StringToDate( textFrame.mtgStartDate && textFrame.startTime);
  18.         temp.mtgDuration := StringToDate( textFrame.mtgDuration) - StringToDate( "0");
  19.         temp.mtgText := textFrame.title;
  20.         if textFrame.text <> nil then
  21.             begin
  22.             temp.notesdata := StringToParagraphArray( store, textFrame.text, kWSforCalendarNote);
  23.             end;
  24.         if textFrame.alarm <> nil then
  25.             begin
  26.             temp.mtgAlarm := Floor(StringToNumber (textFrame.alarm));
  27.             // meeting alarms are an absolute time rather than a relative one so convert if necessary
  28.             if temp.mtgAlarm < 50000 then
  29.                 temp.mtgAlarm := temp.mtgStartDate - temp.mtgAlarm;
  30.             end;
  31.         end;
  32.  
  33.     return temp;
  34.     end;
  35.  
  36. CalendarBuildRepeatMeeting :=    func( textFrame, store)
  37.     begin
  38.     local temp;
  39.     if textFrame.mtgStartDate and textFrame.startTime and textFrame.mtgDuration and textFrame.title then
  40.         begin
  41.         temp := {viewStationery:    'repeatingMeeting};
  42.         temp.MetaDataSoupRef := store:GetSoup ("Repeat Meetings");
  43.         temp.mtgStartDate := StringToDate( textFrame.mtgStartDate && textFrame.startTime);
  44.         temp.mtgDuration := StringToDate( textFrame.mtgDuration) - StringToDate( "0");
  45.         temp.mtgText := textFrame.title;
  46.         if textFrame.text <> nil then
  47.             begin
  48.             temp.notesdata := StringToParagraphArray( store, textFrame.text, kWSforCalendarNote);
  49.             end;
  50.         if textFrame.alarm <> nil then
  51.             temp.mtgAlarm := Floor(StringToNumber (textFrame.alarm));
  52.         if textFrame.mtgStopDate <> nil then
  53.             temp.mtgStopDate := Floor(StringToNumber (textFrame.mtgStopDate));
  54.         if textFrame.mtgInfo <> nil then
  55.             temp.mtgInfo := Floor(StringToNumber (textFrame.mtgInfo));
  56.         if textFrame.repeatType <> nil then
  57.             temp.repeatType := Floor(StringToNumber (textFrame.repeatType));
  58.         end;
  59.     return temp;
  60.     end;
  61.  
  62. CalendarBuildDayNote :=    func( textFrame, store)
  63.     begin
  64.     local temp;
  65.     if textFrame.mtgStartDate and textFrame.title then
  66.         begin
  67.         temp := {viewStationery:    'CribNote};
  68.         temp.MetaDataSoupRef := store:GetSoup ("Calendar Notes");
  69.         temp.mtgStartDate := StringToDate( textFrame.mtgStartDate && textFrame.startTime);
  70.         if textFrame.mtgDuration <> nil then
  71.             temp.mtgDuration := StringToDate( textFrame.mtgDuration) - StringToDate( "0");
  72.         temp.mtgText := textFrame.title;
  73.         if textFrame.alarm <> nil then
  74.             temp.mtgAlarm := Floor(StringToNumber (textFrame.alarm));
  75.         end;
  76.     return temp;
  77.     end;
  78.  
  79. CalendarBuildRepeatDayNote :=    func( textFrame, store)
  80.     begin
  81.     local temp;
  82.     if textFrame.mtgStartDate and textFrame.title then
  83.         begin
  84.         temp := {viewStationery:    'CribNote};
  85.         temp.MetaDataSoupRef := store:GetSoup ("Repeat Notes");
  86.         temp.mtgText := textFrame.title;
  87.         temp.mtgStartDate := StringToDate( textFrame.mtgStartDate && textFrame.startTime);
  88.         if textFrame.mtgDuration <> nil then
  89.             temp.mtgDuration := StringToDate( textFrame.mtgDuration) - StringToDate( "0");
  90.         if textFrame.mtgStopDate <> nil then
  91.             temp.mtgStopDate := Floor(StringToNumber (textFrame.mtgStopDate));
  92.         if textFrame.mtgInfo <> nil then
  93.             temp.mtgInfo := Floor(StringToNumber (textFrame.mtgInfo));
  94.         if textFrame.repeatType <> nil then
  95.             temp.repeatType := Floor(StringToNumber (textFrame.repeatType));
  96.         if textFrame.alarm <> nil then
  97.             temp.mtgAlarm := Floor(StringToNumber (textFrame.alarm));
  98.         end;
  99.     return temp;
  100.     end;
  101.  
  102. CalendarBuildToDoItem :=    func( textFrame, store)
  103.     begin
  104.     local temp;
  105.     if textFrame.mtgStartDate and textFrame.data then
  106.         begin
  107.         temp := {viewStationery:    'todoitem};
  108.         temp.MetaDataSoupRef := store:GetSoup ("To do");
  109.         temp.mtgStartDate := StringToDate( textFrame.mtgStartDate && textFrame.startTime);
  110.         if textFrame.mtgpriority <> nil then
  111.             temp.mtgpriority := Floor(StringToNumber (textFrame.mtgpriority) - 1);
  112.         if textFrame.mtgDone <> nil then
  113.             if StrEqual (textFrame.mtgDone, "Yes") then
  114.                 temp.mtgDone := true;
  115.             else
  116.                 temp.mtgDone := nil;
  117.         end;
  118.     return temp;
  119.     end;
  120.  
  121.  
  122.  
  123. // Here's the code for importing the calendar. It's separate so it can be referenced
  124. // from two slots without duplicating code. This imports a text frame that could
  125. // generate an entry for any of 5 soups.
  126. CalendarTabImport := func( textFrame, store )
  127.     begin
  128.     local soupFrame;
  129.     if textFrame.itemType <> nil then
  130.         begin
  131.         if StrEqual( textFrame.itemType, "Meeting") then
  132.             soupFrame := :BuildMeeting( textFrame, store);
  133.         else if StrEqual( textFrame.itemType, "Repeating Meeting") then
  134.             soupFrame := :BuildRepeatMeeting( textFrame, store);
  135.         else if StrEqual( textFrame.itemType, "Day Note") then
  136.             soupFrame := :BuildDayNote( textFrame, store);
  137.         else if StrEqual( textFrame.itemType, "Repeating Day Note") then
  138.             soupFrame := :BuildRepeatDayNote( textFrame, store);
  139.         else if StrEqual( textFrame.itemType, "To Do Item") then
  140.             soupFrame := :BuildToDoItem( textFrame, store);
  141.         end
  142.     else
  143.         begin
  144.         if textFrame.mtgStartDate <> nil then
  145.             if textFrame.title <> nil then
  146.                 if textFrame.freq <> nil then
  147.                     if textFrame.mtgDuration <> nil then
  148.                         soupFrame := :BuildRepeatMeeting( textFrame, store);
  149.                     else
  150.                         soupFrame := :BuildRepeatDayNote( textFrame, store);
  151.                 else
  152.                     if (textFrame.mtgpriority <> nil) or (textFrame.mtgdone <> nil) then
  153.                         soupFrame := :BuildToDoItem( textFrame, store);
  154.                     else if textFrame.mtgDuration <> nil then
  155.                         soupFrame := :BuildMeeting( textFrame, store);
  156.                     else
  157.                         soupFrame := :BuildDayNote( textFrame, store);
  158.         end;
  159.  
  160.     :entryValidation(soupFrame, store);
  161.     end;
  162.  
  163. //-------------------------------------------------------------------------------------------------
  164. //                        E X P O R T
  165. //-------------------------------------------------------------------------------------------------
  166. ExportCalendarText := func (data)
  167.     begin
  168.         local result := nil;
  169.         if data then
  170.             begin
  171.             // First make an array of the text slot indexes in the order of the top value in the viewbounds
  172.             // Need to do this because the paragraphs are stored in the array in the order the user enters them,
  173.             // not in the top to bottom order I want.
  174.             local tops := [];
  175.             foreach index, datum in data do
  176.                 if datum.text and not datum.copyProtection then
  177.                     AddArraySlot (tops, {top: datum.viewbounds.top, index: index});
  178.             Sort(tops, '|<|, 'top);
  179.  
  180.             foreach which in tops do
  181.                 begin
  182.                 theText := data[which.index].text;
  183.                 if result then
  184.                     result := Stringer([result, "\n", theText]);
  185.                 else
  186.                     result := theText;
  187.                 end;
  188.             end;
  189.         if not result then
  190.             result := "";
  191.         return result;
  192.     end;
  193.  
  194. CalendarCommonExport := func( soupFrame, store )
  195.     begin
  196.     // Possible values for viewStationery are: 'meeting, 'repeatingMeeting, 'exceptionMeeting, 'CribNote, 'todoitem
  197.     local textFrame := {originalFrame: soupFrame};
  198.  
  199.     if soupFrame.viewStationery = 'todoitem then
  200.         begin
  201.         if (soupFrame.mtgpriority) and (soupFrame.mtgpriority <> 3) then
  202.             textFrame.mtgpriority := :getPriorityString( soupFrame);
  203.         textFrame.completed := :getCompletedString(soupFrame);
  204.         end;
  205.  
  206.     if (soupFrame.mtgDuration) then
  207.         begin
  208.         if (soupFrame.mtgDuration >= 60) then
  209.             textFrame.mtgDuration := TimeStr(soupFrame.mtgDuration, 129);
  210.         else
  211.             if (soupFrame.mtgDuration <> 0) then
  212.                 textFrame.mtgDuration := "0:" & TimeStr(soupFrame.mtgDuration, 2);
  213.         end;
  214.  
  215.     if (soupFrame.mtgStartDate) then
  216.         textFrame.startTime := HourMinute( soupFrame.mtgStartDate);
  217.  
  218.     if (soupFrame.mtgStartDate) then
  219.         begin
  220.         textFrame.mtgStartDate := ShortDateStr(soupFrame.mtgStartDate, 0);
  221.         textFrame.day := LongDateStr(soupFrame.mtgStartDate, 2);
  222.         end;
  223.  
  224.     if (soupFrame.notesdata) then
  225.         textFrame.text := :exportText(soupFrame.notesdata);
  226.  
  227.     local title := :getStandardTitleString(soupFrame);
  228.     if title <> nil then
  229.         textFrame.title := title;
  230.  
  231.     local itemType := :getStandardTypeString(soupFrame);
  232.     if itemType <> nil then
  233.         textFrame.itemType := itemType;
  234.  
  235.     textFrame;
  236.     end;
  237.  
  238.  
  239. CalendarTabExport := func( soupFrame, store )
  240.     begin
  241.     local textFrame := :commonExport (soupFrame, store);
  242.     if (soupFrame.repeatTemplate) then
  243.         begin
  244.         textFrame.freq := NumberStr(soupFrame.repeatTemplate.mtgStartDate)
  245.                             && NumberStr( soupFrame.repeatTemplate.mtgStopDate)
  246.                             && NumberStr( soupFrame.repeatTemplate.repeatType)
  247.                             && NumberStr( soupFrame.repeatTemplate.mtgInfo);
  248.         end;
  249.     if not (textFrame.itemType Exists) then
  250.         textFrame := nil;
  251.     if (soupFrame.mtgAlarm) then
  252.         textFrame.alarm := NumberStr (soupFrame.mtgAlarm);
  253.     textFrame;
  254.     end;
  255.  
  256. CalendarBrowserExport := func( soupFrame, store )
  257.     begin
  258.     local textFrame := :commonExport (soupFrame, store);
  259.     local viewStationery := soupFrame.viewStationery;
  260.  
  261.     // A few special types of entries are only exported for the browser/editor
  262.     local itemType := :getNonStandardTypeString(soupFrame);
  263.     if (itemType <> nil) then
  264.         textFrame.itemType := itemType;
  265.     if viewStationery = 'para then
  266.         if soupFrame.text <> nil then
  267.             textFrame.title := :getNonStandardTitleString(soupFrame);
  268.  
  269.     if (soupFrame.mtgAlarm) then
  270.         begin
  271.         local alarm := soupFrame.mtgAlarm;
  272.         textFrame.alarm := "";
  273.         // meetings have an absolute date rather than a relative date so I convert it here
  274.         if alarm > 50000 and viewStationery = 'meeting then
  275.             alarm := soupFrame.mtgStartDate - alarm;
  276.         if (viewStationery = 'CribNote) then
  277.             textFrame.alarm := DateNTime (soupFrame.mtgStartDate - alarm);
  278.         else if alarm > 50000 then            // Still need this for not repeating daynotes
  279.             textFrame.alarm := DateNTime (alarm);
  280.         else if alarm > (60 * 24) then
  281.             textFrame.alarm := NumberStr (alarm / (60 * 24)) && "days ";
  282.         else if alarm > 60 then
  283.             textFrame.alarm := textFrame.alarm & NumberStr (alarm / 60) && "hours ";
  284.         else if alarm <> 0 then
  285.             textFrame.alarm := textFrame.alarm & NumberStr (alarm) && "minutes";
  286.         end;
  287.  
  288.     if soupFrame.mtgStopDate <> nil and soupFrame.mtgStopDate <> kForever then
  289.         textFrame.mtgStopDate := ShortDateStr (soupFrame.mtgStopDate, 0);
  290.  
  291.     if (soupFrame.mtgInfo <> nil) and (soupFrame.repeatType <> nil) then
  292.         textFrame.freq := RepeatInfoToText (soupFrame.mtgInfo, soupFrame.repeatType);
  293.  
  294.     textFrame;
  295.     end;
  296.  
  297. //-------------------------------------------------------------------------------------------------
  298. //                        M E T A  D A T A
  299. //-------------------------------------------------------------------------------------------------
  300. calendarmetadata :=  {
  301.     soup: "Calendar",
  302.     name: "Date Book",
  303.     version: 2,
  304.     dependents: ["Repeat Meetings",
  305.                  "Calendar Notes",
  306.                  "Repeat Notes",
  307.                  "To do"],
  308.     soupinfo: {applications: ['Calendar], itemNames: ["date", "dates"]},
  309.     soupindicies: [{structure: 'slot, path: 'mtgStartDate, type: 'Int},
  310.                    {structure: 'slot, path: 'mtgAlarm, type: 'Int}],
  311.  
  312.     defaultDefinitions: {
  313.         itemType:        {label:"Type",            type:'itemType,                        compareScript: 'typeCompare},
  314.         mtgStartDate:    {label:"Date",            type:'dateNTime,                    compareScript: 'startDateCompare},
  315.         day:            {label:"Day",            type:'day,            path:'mtgStartDate},
  316.         startTime:        {label:"Start Time",    type:'time,            path:'mtgStartDate},
  317.         title:            {label:"Title",                                                compareScript: 'titleCompare},
  318.         mtgDuration:    {label:"Duration",        type:'duration},
  319.         freq:            {label:"Repeat info",    type:'date,            uneditable: true,     unsearchable: true},
  320.         mtgStopDate:    {label:"Stop Date",        type:'dateNTime},
  321.         text:            {label:"Notes",            type:'text,                            compareScript: 'notesCompare,    uneditable: true},
  322.         alarm:            {label:"Alarm",                             path:'mtgAlarm,                    unsearchable: true},
  323.         mtgPriority:    {label:"Priority",                                            compareScript: 'priorityCompare},
  324.         completed:        {label:"Completed",                         path:'mtgDone,    compareScript: 'completedCompare},
  325.         },
  326.  
  327.     displayInfo: [
  328.         'itemType,
  329.         'mtgStartDate,
  330.         'day,
  331.         'startTime,
  332.         'title,
  333.         'mtgDuration,
  334.         {slot: 'freq,             hidden: TRUE},
  335.         {slot: 'mtgStopDate,     hidden: TRUE},
  336.         {slot: 'text,             hidden: TRUE},
  337.         {slot: 'alarm,             hidden: TRUE},
  338.         {slot: 'mtgPriority,     hidden: TRUE},
  339.         {slot: 'completed,         hidden: TRUE},
  340.         ],
  341.  
  342.     // This was never fully debugged since it's not supported in Newton Connection.
  343.     // It's provided here for completness.
  344.     editInfo: [
  345.         'itemType,
  346.         'mtgStartDate,
  347.         'startTime,
  348.         'mtgDuration,
  349.         'title,
  350.         'text,
  351.         'alarm,
  352.         'freq,
  353.         'mtgStopDate,
  354.         'mtgPriority,
  355.         'completed,
  356.         ],
  357.  
  358.     exportInfo: [
  359.         'itemType,
  360.         'day,
  361.         'mtgStartDate,
  362.         'startTime,
  363.         'mtgDuration,
  364.         'title,
  365.         'text,
  366.         'alarm,
  367.         'freq,
  368.         'mtgStopDate,
  369.         'mtgPriority,
  370.         'completed
  371.         ],
  372.  
  373.     editTypes:{
  374.             itemType: {
  375.                 type:'PICKLIST,
  376.                 choices:[
  377.                     "Day Note",
  378.                     "Meeting",
  379.                     "Repeating Day Note",
  380.                     "Repeating Meeting",
  381.                     "To Do Item",
  382.                     ]
  383.                 },
  384.             },
  385.  
  386.     compareScripts: {
  387.         startDateCompare:
  388.             func(entry, args)
  389.                 begin
  390.                 local result;
  391.                 if entry.mtgStartDate exists then
  392.                     if (args.searchType <= 3) then
  393.                         result := ShortDateStr(entry.mtgStartDate, 0);
  394.                     else
  395.                         result := entry.mtgStartDate - (entry.mtgStartDate mod 1440);
  396.                 return result;
  397.                 end,
  398.         completedCompare:
  399.             func(entry, args)
  400.                 begin
  401.                 local result;
  402.                 if (entry.viewStationery = 'todoitem) then
  403.                     result := :getCompletedString(entry);
  404.                 return result;
  405.                 end,
  406.         priorityCompare:
  407.             func(entry, args)
  408.                 begin
  409.                 return :getPriorityString( entry);
  410.                 end,
  411.         notesCompare:
  412.             func(entry, args)
  413.                 begin
  414.                 local result;
  415.                 if (entry.notesdata) then
  416.                     result := :exportText(entry.notesdata);
  417.                 return result;
  418.                 end,
  419.         titleCompare:
  420.             func(entry, args)
  421.                 begin
  422.                 return :getTitleString(entry);
  423.                 end,
  424.         typeCompare:
  425.             func(entry, args)
  426.                 begin
  427.                 return :getTypeString(entry);
  428.                 end,
  429.         },
  430.  
  431.     buildMeeting: CalendarBuildMeeting,
  432.     buildRepeatMeeting: CalendarBuildRepeatMeeting,
  433.     buildDayNote: CalendarBuildDayNote,
  434.     buildRepeatDayNote: CalendarBuildRepeatDayNote,
  435.     buildToDoItem: CalendarBuildToDoItem,
  436.     tabImport:     calendarTabImport,
  437.     editImport: calendarTabImport,
  438.  
  439.     commonExport: CalendarCommonExport,
  440.     browseEditExport: CalendarBrowserExport,
  441.     exportText: ExportCalendarText,
  442.  
  443.     tabExport: CalendarTabExport,
  444.     editExport: func( soupFrame, store )
  445.             :browseEditExport ( soupFrame, store ),
  446.     browserExport: func( soupFrame, store )
  447.             :browseEditExport ( soupFrame, store ),
  448.  
  449.     // ----------------------------------------------------------------------------------------
  450.     //    Functions used in common by CompareScripts and Export
  451.     // ----------------------------------------------------------------------------------------
  452.     getCompletedString: func( entry)
  453.         begin
  454.         local result;
  455.         if (entry.mtgDone) then
  456.             result := "Yes";
  457.         else
  458.             result := "No";
  459.         return result;
  460.         end,
  461.  
  462.     getPriorityString: func( entry)
  463.         begin
  464.         local result;
  465.         if (entry.mtgPriority) and (entry.mtgPriority <> 3) then
  466.             result := NumberStr(entry.mtgpriority + 1);
  467.         return result;
  468.         end,
  469.  
  470.     getStandardTitleString: func( entry)
  471.         begin
  472.         local result;
  473.         local viewStationery := entry.viewStationery;
  474.         if (viewStationery = 'meeting) or (viewStationery = 'repeatingMeeting) or (viewStationery = 'CribNote) then
  475.             result := entry.mtgText;
  476.         else if viewStationery = 'todoitem then
  477.             result := :exportText(entry.data);
  478.         return result;
  479.         end,
  480.  
  481.     getNonStandardTitleString: func( entry)
  482.         begin
  483.         local result;
  484.         if (entry.viewStationery = 'para) and (entry.text <> nil) then
  485.             result := entry.text;
  486.         return result;
  487.         end,
  488.  
  489.     getTitleString: func( entry)
  490.         begin
  491.         local result := :getStandardTitleString(entry);
  492.         if (result = nil) then
  493.             result := :getStandardTitleString(entry);
  494.         return result;
  495.         end,
  496.  
  497.     getStandardTypeString: func(entry)
  498.         begin
  499.         local result;
  500.         local viewStationery := entry.viewStationery;
  501.         if viewStationery = 'meeting then
  502.             result := "Meeting";
  503.         else if (viewStationery = 'repeatingMeeting)
  504.             or (viewStationery = 'exceptionMeeting) then
  505.             result := "Repeating Meeting";
  506.         else if viewStationery = 'todoitem then
  507.             result := "To Do Item";
  508.         else if viewStationery = 'CribNote then
  509.             if (entry.repeatType <> nil) or (entry.mtgStopDate <> nil) or (entry.repeatTemplate <> nil) or (entry.freq <> nil) then // this is repeating crib note
  510.                 result := "Repeating Day Note";
  511.             else
  512.                 result := "Day Note";
  513.         return result;
  514.         end,
  515.  
  516.     getNonStandardTypeString: func(entry)
  517.         begin
  518.         local result;
  519.         local viewStationery := entry.viewStationery;
  520.         if viewStationery = nil then
  521.             begin
  522.             if entry.ink <> nil then
  523.                 result := "Ink";
  524.             else
  525.                 result := "Unknown";
  526.             end
  527.         else if viewStationery = 'poly then
  528.             result := "Graphic";
  529.         else if viewStationery = 'para then
  530.             result := "Paragraph";
  531.         return result;
  532.         end,
  533.  
  534.     getTypeString: func(entry)
  535.         begin
  536.         local result := :getStandardTypeString(entry);
  537.         if (result = nil) then
  538.             result := :getNonStandardTypeString(entry);
  539.         return result;
  540.         end,
  541.  
  542.     // ----------------------------------------------------------------------------------------
  543.     //        V A L I D A T I O N
  544.     // ----------------------------------------------------------------------------------------
  545.     // These frames describe the slots of the soups frames and how they should be validated
  546.     calendarValidation: {
  547.         viewStationery: {validType: ['meeting, 'repeatingMeeting, 'cribNote], required: true},
  548.         mtgStartDate:        {validType: 'int, required: true},
  549.         mtgDuration:        {validType: 'int, required: true},
  550.         mtgText:                {validType: 'string, required: true},
  551.         mtgStopDate:        {validType: 'int},
  552.         repeatType:            {validType: [kDayOfWeek, kWeekInMonth, kDateInMonth, kDateinYear, kPeriod, kNever]},
  553.         mtgInfo:                {validType: 'int},
  554.         mtgAlarm:                {validType: 'int},
  555.         notesData:            {validType: 'note, noteMaxWidth: 224, nilOK: true},
  556.         viewBounds:            {validType: 'rect},
  557.         },
  558.  
  559.     dayNoteValidation: {
  560.         viewStationery: {validType: ['cribNote], required: true},
  561.         mtgStartDate:        {validType: 'int, required: true},
  562.         mtgDuration:        {validType: 'int},
  563.         mtgText:                {validType: 'string, required: true},
  564.         mtgStopDate:        {validType: 'int},
  565.         repeatType:            {validType: [kDayOfWeek, kWeekInMonth, kDateInMonth, kDateinYear, kPeriod, kNever]},
  566.         mtgInfo:                {validType: 'int},
  567.         mtgAlarm:                {validType: 'int},
  568.         notesData:            {validType: 'note, noteMaxWidth: 224, nilOK: true},
  569.         viewBounds:            {validType: 'rect},
  570.         },
  571.  
  572.     todoValidation: {
  573.         viewStationery: {validType: ['todoitem], required: true},
  574.         mtgStartDate:        {validType: 'int, required: true},
  575.         mtgDone:                {validType: [nil, true], required: true, nilOK: true},
  576.         mtgPriority:        {validType: [0, 1, 2, 3], required: true},
  577.         data:                        {validType: 'note, noteMaxWidth: 204, nilOK: true},
  578.         },
  579.  
  580.     calendarParaFormat: {
  581.         lineHeight:                16,
  582.         maxRight:                    224,
  583.         defaultFontSpec:    bor(tsSimple,tsSize(12)),
  584.         maxBottom:                151,
  585.         },
  586.  
  587.     toDoParaFormat: {
  588.         lineHeight:                16,
  589.         maxRight:                    204,
  590.         defaultFontSpec:    bor(tsSimple,tsSize(12)),
  591.         maxBottom:                1000,
  592.         },
  593.  
  594.     entryValidation: func (toValidate, store)
  595.         begin
  596.         if not IsFrame (toValidate) then
  597.             return "a Calendar entry was invalid";
  598.         if toValidate.viewStationery = nil then
  599.             begin
  600.             if toValidate.ink = nil then
  601.                 return "a Calendar entry was invalid";
  602.             else
  603.                 // it's ink
  604.                 toValidate := VerifyParagraphScript (toValidate, 226, "Calendar");
  605.             end;
  606.         else if (toValidate.viewStationery = 'para) or
  607.                         (toValidate.viewStationery = 'poly) then
  608.             toValidate := VerifyParagraphScript (toValidate, 226, "Calendar");
  609.         else if toValidate.viewStationery = 'todoitem then
  610.             begin
  611.             toValidate := ValidateSlots ("Calendar", toValidate, todoValidation);
  612.             if IsFrame (toValidate) then // It passed
  613.                 toValidate := ValidateNotes(toValidate, 'data, toDoParaFormat, store);
  614.             // only now can we ensure that it has a height
  615.             // DockerFlowParagraph the title, loop
  616.             local deepest;
  617.  
  618.             if (toValidate.height <> nil) and (ClassOf(toValidate.height) = 'Int) then
  619.                 deepest := toValidate.height;
  620.             else
  621.                 deepest := 0;
  622.  
  623.             if IsArray(toValidate.data) then
  624.                 foreach paragraph in toValidate.data do
  625.                     if paragraph.viewBounds.bottom > deepest then
  626.                         deepest := paragraph.viewBounds.bottom;
  627.  
  628.             toValidate.height := deepest;
  629.             end
  630.         else
  631.             begin
  632.             if toValidate.viewStationery = 'CribNote then
  633.                 toValidate := ValidateSlots ("Calendar", toValidate, dayNoteValidation);
  634.             else
  635.                 toValidate := ValidateSlots ("Calendar", toValidate, calendarValidation);
  636.             if IsFrame(toValidate) then
  637.                 begin
  638.                 // Now do the special validation
  639.                 if toValidate.mtgStopDate Exists then
  640.                     if toValidate.mtgStopDate < toValidate.mtgStartDate and toValidate.mtgStopDate <> kForever then
  641.                         return "a Calendar entry contained a start date that was after the stop date";
  642.  
  643.                 // Make sure the high 16 bits of the mtgInfo are 0
  644.                 if toValidate.mtgInfo Exists then
  645.                     toValidate.mtgInfo := band (toValidate.mtgInfo, 0xFFFF);
  646.  
  647.                 if ((toValidate.repeatType Exists) and not (toValidate.mtgInfo Exists)) or
  648.                      ((not toValidate.repeatType Exists) and (toValidate.mtgInfo Exists)) then
  649.                     return "a Calendar entry contained an invalid repeating meeting";
  650.  
  651.                 toValidate := ValidateNotes(toValidate, 'notesData, calendarParaFormat, store);
  652.  
  653.                 end;
  654.             end;
  655.         return toValidate;
  656.         end,
  657. };
  658.  
  659. //=============================================================================
  660. // RepeatMeetings
  661. //=============================================================================
  662.  
  663. RepeatMeetingsMeta :=  {
  664.     soup: "Repeat Meetings",
  665.     name: "Repeating Meetings",
  666.     editwith: 'Calendar,
  667.     version: 2,
  668.     soupInfo: {applications: ['Calendar], itemNames: ["date", "dates"]},
  669.     soupIndicies: [{structure: 'slot, path: 'mtgStopDate, type: 'Int},
  670.                    {structure: 'slot, path: 'mtgAlarm, type: 'Int}],
  671. };
  672.  
  673. //=============================================================================
  674. // RepeatNotes
  675. //=============================================================================
  676.  
  677. RepeatNotesMeta :=  {
  678.     soup: "Repeat Notes",
  679.     name: "Repeating Notes",
  680.     editwith: 'Calendar,
  681.     version: 2,
  682.     soupInfo: {applications: ['Calendar], itemNames: ["meeting note", "meeting notes"]},
  683.     soupIndicies: [{structure: 'slot, path: 'mtgStopDate, type: 'Int},
  684.                    {structure: 'slot, path: 'mtgAlarm, type: 'Int}],
  685. };
  686.  
  687. //=============================================================================
  688. // TodoSoup
  689. //=============================================================================
  690.  
  691. #define todoWidth 198
  692.  
  693. TodoMeta :=  {
  694.     soup: "To do",
  695.     name: "To do",
  696.     editwith: 'Calendar,
  697.     version: 2,
  698.     soupInfo: {applications: ['Calendar], itemNames: ["todo item", "todo items"]},
  699.     soupIndicies: [{structure: 'slot, path: 'mtgStartDate, type: 'Int},
  700.                    {structure: 'slot, path: 'mtgAlarm, type: 'Int}],
  701.  
  702. };
  703.  
  704. //=============================================================================
  705. // CalendarNotes
  706. //=============================================================================
  707.  
  708. CalendarNotesMeta :=  {
  709.     soup: "Calendar Notes",
  710.     name: "Calendar Notes",
  711.     editwith: 'Calendar,
  712.     version: 2,
  713.     soupInfo: {applications: ['Calendar], itemNames: ["meeting note", "meeting notes"]},
  714.     soupindicies: [{structure: 'slot, path: 'mtgStartDate, type: 'Int},
  715.                    {structure: 'slot, path: 'mtgAlarm, type: 'Int}],
  716. };
  717.  
  718.